import java.util.ArrayList;
import java.util.List;

class Token {
  static final String ILLEGAL = "ILLEGAL";
  static final String EOL = "EOL";
  static final String IDENT = "IDENT";
  static final String WITH = "WITH";
  static final String TRIGGER = "TRIGGER";
  static final String SUBJECT = "SUBJECT";
  static final String BUILTIN = "BUILTIN";
  static final String COLON = "COLON";

  private String type;
  private String literal;

  Token() {

  }

  Token(String type, char literal) {
    setType(type);
    setLiteral(literal);
  }

  Token(String type, String literal) {
    setType(type);
    setLiteral(literal);
  }

  public void setType(String type) {
    this.type = type;
  }

  public String getType() {
    return type;
  }

  public void setLiteral(char literal) {
    this.literal = String.valueOf(literal);
  }

  public void setLiteral(String literal) {
    this.literal = literal;
  }

  public String getLiteral() {
    return literal;
  }

  private static boolean containsBuiltIn(String ident) {
    try {
      BuiltIn.valueOf(ident.toUpperCase());
      return true;
    } catch (IllegalArgumentException e) {
      return false;
    }
  }

  public static String lookupIdent(String ident, IAction actions) {
    var triggers = actions.getTriggers();
    var subjects = actions.getSubjects();
    if (triggers.contains(ident)) return TRIGGER;
    if (subjects.contains(ident)) return SUBJECT;
    if (containsBuiltIn(ident)) return BUILTIN;
    if (ident.equalsIgnoreCase(WITH)) return WITH;
    return IDENT;
  }

  @Override
  public String toString() {
    return "Token{" + "type='" + type + '\'' + ", literal='" + literal + '\'' + '}';
  }
}

public class Lexer {
  private final String input;
  private IAction actions;
  private int position;
  private int current;
  private char ch;

  private final List<Token> tokens = new ArrayList<>();

  Lexer(String input, IAction actions) {
    this.input = input;
    setActions(actions);
    advance();

    for (var tok = next(); !(tok.getType().equals(Token.EOL)); tok = next()) {
      tokens.add(tok);
    }
  }

  public void setActions(IAction actions) {
    this.actions = actions;
  }

  public List<Token> getTokens() {
    return tokens;
  }

  private void advance() {
    if (current >= input.length()) {
      ch = 0;
    } else {
      ch = input.charAt(current);
    }
    position = current;
    current += 1;
  }

  private void skipWhitespace() {
    while (ch == ' ' || ch == '\t' || ch == '\r') {
      advance();
    }
  }

  private boolean isLetter(char ch) {
    return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z';
  }

  private String readIdent() {
    var pos = position;
    while (isLetter(ch)) {
      advance();
    }
    return input.substring(pos, position);
  }

  private Token next() {
    Token tok;
    skipWhitespace();
    switch (ch) {
      case ':' -> tok = new Token(Token.COLON, ch);
      case 0 -> tok = new Token(Token.EOL, "");
      default -> {
        if (isLetter(ch)) {
          tok = new Token();
          tok.setLiteral(readIdent());
          tok.setType(Token.lookupIdent(tok.getLiteral(), actions));
          return tok;
        } else {
          tok = new Token(Token.ILLEGAL, ch);
        }
      }
    }

    advance();
    return tok;
  }

}
